home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD 2.1 / Amiga Developer CD v2.1.iso / Reference / Amiga_Mail_Vol1 / IFF / 8SVX < prev    next >
Encoding:
Text File  |  1999-10-27  |  14.7 KB  |  419 lines

  1. (c)  Copyright 1989-1999 Amiga, Inc.   All rights reserved.
  2. The information contained herein is subject to change without notice, and 
  3. is provided "as is" without warranty of any kind, either expressed or implied.  
  4. The entire risk as to the use of this information is assumed by the user.
  5.  
  6.  
  7.  
  8.                    8SVX: Playing Samples Larger Than 128K
  9.  
  10.                                by Dan Baker
  11.  
  12.  
  13. The Amiga's audio hardware contains four digital-to-analog converters 
  14. (DACs) capable of playing back digital sound samples with 8-bit resolution.  
  15. On the Amiga sound samples are usually stored in the 8SVX format which is 
  16. the IFF file standard for 8-bit samples.  The 8SVX standard allows samples 
  17. up to 2 gigabytes long.  However, the length registers of the DACs only 
  18. allow samples up to 128K bytes.  In order to play back a sample larger than 
  19. 128K, you break it up into smaller pieces and send multiple requests to the 
  20. audio device.  The program listed below shows you how to use this technique.
  21.  
  22. The register map in Appendix B of the Hardware Manual shows that all the 
  23. audio length registers are 16 bits which gives a maximum length of 65,536.
  24. However, since the audio hardware can DMA two bytes at a time, the length 
  25. register is set up to represent the word count of the sample, not the 
  26. byte count.  Hence, the maximum length for a sample is 2 times 65,536 or 
  27. 128K bytes.
  28.  
  29. Audio sample data in the 8SVX format is stored as a standard IFF Chunk
  30. of type BODY.  The Chunk size is given by the field ckSize which is a
  31. LONG variable.  So the maximum sample size in an IFF Chunk is just over 
  32. 2 gigabytes.  Even larger samples might be possible by using a CAT
  33. or LIST.  See the IFF manual for more details.
  34.  
  35. The original Amiga had 512K of chip RAM, much of which would be busy doing
  36. graphics work, so the 128K audio sample size limit was a good design decision.
  37. Because of memory limits, most early Amiga samples were short sound
  38. effects less than 128K.
  39.  
  40. But the newest Amigas have 1 megabyte of chip RAM and sometimes additional
  41. expansion RAM.  This trend will probably continue as memory prices fall.
  42. So the idea of supporting 2 gigabyte samples under IFF is not far-fetched.
  43.  
  44. The program listed below shows you how to play back 8SVX samples larger 
  45. than 128K on the Amiga by breaking up the sample into smaller pieces.  
  46. The program divides large samples into sections of 51200 bytes. 
  47. The samples are sent to the audio device using double-buffering.  That is,
  48. there are always two IO requests in the queue.  A Wait(), GetMsg() loop puts 
  49. the progam to sleep until the current audio request finishes.  
  50.  
  51. When the program wakes up, the next request in the queue begins to play 
  52. immediately.  While it plays, new sample data is copied into chip memory.  
  53. The request that just ended is then reused by placing it back in the queue 
  54. and the program goes through the Wait(), GetMsg() loop again.  Note that
  55. there is nothing special about using 51200-byte samples.  Any size up to 
  56. 128K would work but 51200 is used to conserve chip memory. 
  57.  
  58. The IFF parsing used by the program is very simple.  The FORM is first
  59. inspected for the file size and the file is read into memory.  A switch 
  60. statement is used to cull the VHDR and BODY Chunks from the file and 
  61. pointers are set up.  Other Chunks are skipped.  CATs, LISTs and nested 
  62. FORMs are not supported.  If the BODY Chunk has both a one-shot and 
  63. continuous part, only the one-shot part is used.  Similarly, only
  64. the first ocatve part is played in a multi-octave sample.
  65.  
  66. The audio device is handled in the usual way.  Two AudioIO requests and a 
  67. reply port are set up.  Only one audio channel is used.  The channel is 
  68. allocated automatically when the device is opened.  The allocation key is 
  69. set to accept any of the four channels.  The request priority is set to 128 
  70. so that once we have the channel, it cannot be stolen by another task during 
  71. playback.
  72.  
  73. An important part of the program is the setting of the clock constant to 
  74. the right value for both PAL and NTSC systems.  The clock constant, which is 
  75. used in calculating the period of an audio request, is different on PAL and 
  76. NTSC systems.  To get the right value look at the DisplayFlags field of 
  77. GfxBase in the graphics library.
  78.  
  79. By taking adavantage of the audio device's ability to queue up multiple 
  80. IO requests, it is possible to extend the effective sample size limit of 
  81. the Amiga beyond the 128K barrier.  The Amiga's DMA driven audio hardware
  82. can smoothly play back samples of any arbitrary size.
  83.  
  84.  
  85. /*----------------*/
  86. /*   INCLUDES     */
  87. /*----------------*/
  88. #include "exec/types.h"
  89. #include "exec/memory.h"
  90. #include "devices/audio.h"
  91. #include "libraries/dos.h"
  92. #include "libraries/dosextens.h"
  93. #include "graphics/gfxbase.h"
  94. #include "iff/iff.h"
  95. #include "iff/8svx.h"
  96.  
  97. #define VHDR MakeID('V','H','D','R')
  98. #define BODY MakeID('B','O','D','Y')
  99. #define MY8S MakeID('8','S','V','X')
  100.  
  101.        APTR        OpenLibrary();
  102. struct FileHandle *Open();
  103.        ULONG       Read();
  104.        APTR        AllocMem();
  105. struct GfxBase    *GfxBase;
  106. struct MsgPort    *CreatePort();
  107. struct Message    *GetMsg();
  108. struct Task       *FindTask();
  109.        BYTE        SetTaskPri();
  110.        ULONG       OpenDevice();
  111.        ULONG       Wait();
  112.  
  113. void              kill8svx();
  114. void              kill8(); 
  115.  
  116. /*--------------------*/
  117. /*   G L O B A L S    */
  118. /*--------------------*/
  119. struct IOAudio     *AudioIOBptr1,      /* Pointers to Audio IOBs      */
  120.                    *AudioIOBptr2,
  121.                    *Aptr;
  122. struct Message     *msg;               /* Msg, port and device for    */
  123. struct MsgPort     *port;              /* driving audio               */
  124.        ULONG        device;
  125.        UBYTE       *sbase,*fbase;      /* For sample memory allocation */
  126.        ULONG        fsize,ssize;       /* and freeing                  */
  127. struct FileHandle  *v8handle;
  128.        UBYTE        chan1[]  = {  1 }; /* Audio channel allocation arrays */
  129.        UBYTE        chan2[]  = {  2 };
  130.        UBYTE        chan3[]  = {  4 };
  131.        UBYTE        chan4[]  = {  8 };
  132.        UBYTE       *chans[] = {chan1,chan2,chan3,chan4};
  133.  
  134. /*-----------*/
  135. /*  M A I N  */
  136. /*-----------*/
  137. LONG
  138. main(argc,argv)
  139. LONG argc;
  140. char *argv[];
  141. {
  142. /*-------------*/
  143. /* L O C A L S */
  144. /*-------------*/
  145.        char         *fname;               /* File name and data pointer*/
  146.        UBYTE        *p8data;              /* for file read.            */
  147.        ULONG         clock;               /* Clock constant            */
  148.        ULONG         length[2];           /* Sample lengths            */
  149.        BYTE          iobuffer[8],         /* Buffer for 8SVX header    */
  150.                     *psample[2];          /* Sample pointers           */
  151.        Chunk        *p8Chunk;             /* Pointers for 8SVX parsing */
  152.        Voice8Header *pVoice8Header;
  153.        ULONG         x,y,rd8count,speed;  /* Counters, sampling speed   */
  154.        ULONG         wakebit;             /* A wakeup mask              */
  155.        BYTE          oldpri,c;            /* Stuff for bumping priority */
  156.        struct Task  *mt;
  157.  
  158.  
  159. /*-------------*/
  160. /*   C O D E   */
  161. /*-------------*/
  162.  
  163. /*------------------------------*/
  164. /* Check Arguments, Initialize  */
  165. /*------------------------------*/
  166. fbase=0L;sbase=0L;
  167. AudioIOBptr1=0L;AudioIOBptr2=0L;
  168. port=0L;v8handle=0L;device=1L;
  169.  
  170.  
  171. if(argv[1]==NULL) {kill8svx("No file name given.\n");return(1L);}
  172. fname=argv[1];
  173.  
  174. /*---------------------------*/
  175. /* Initialize Clock Constant */
  176. /*---------------------------*/
  177. GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",0L);
  178. if(GfxBase==0L){puts("Can't open graphics library\n");return(1L);}
  179. if(GfxBase->DisplayFlags & PAL) clock=3546895L;        /* PAL clock */
  180. else                            clock=3579545L;        /* NTSC clock */
  181. if(GfxBase)CloseLibrary(GfxBase);
  182.  
  183.  
  184. /*---------------*/
  185. /* Open the File */
  186. /*---------------*/
  187. v8handle=Open(fname,MODE_OLDFILE);
  188. if(v8handle==0)
  189.   {  kill8svx("Can't open 8SVX file.\n");  return(1L);  }
  190.  
  191. /*-------------------------------------------*/
  192. /* Read the 1st 8 Bytes of the File for Size */
  193. /*-------------------------------------------*/
  194. rd8count=Read(v8handle,iobuffer,8);
  195. if(rd8count==-1){kill8svx ("Read error.\n");return(1L);}
  196. if(rd8count<8)  {kill8svx ("Not an IFF 8SVX file, too short\n");return(1L);}
  197.  
  198. /*-----------------*/
  199. /* Evaluate Header */
  200. /*-----------------*/
  201. p8Chunk=(Chunk *)iobuffer;
  202. if( p8Chunk->ckID != FORM )  {kill8svx("Not an IFF FORM.\n");return(1L);}
  203.  
  204. /*--------------------------*/
  205. /* Allocate Memory for File */
  206. /*  and Read it in.         */
  207. /*--------------------------*/
  208. fbase= (UBYTE *)AllocMem(fsize=p8Chunk->ckSize , MEMF_PUBLIC|MEMF_CLEAR);
  209. if(fbase==0)    {kill8svx("No memory for read.\n");return(1L);}
  210. p8data=fbase;
  211.  
  212. rd8count=Read(v8handle,p8data,p8Chunk->ckSize);
  213. if(rd8count==-1)
  214.    {kill8svx ("Read error.\n");return(1L);}
  215. if(rd8count<p8Chunk->ckSize)
  216.    {kill8svx ("Malformed IFF, too short.\n");return(1L);}
  217. /*-------------------*/
  218. /* Evaluate IFF Type */
  219. /*-------------------*/
  220. if(MakeID( *p8data, *(p8data+1) , *(p8data+2) , *(p8data+3) ) != MY8S )
  221.   {kill8svx("Not an IFF 8SVX file.\n");return(1L);}
  222.  
  223. /*----------------------*/
  224. /* Evaluate 8SVX Chunks */
  225. /*----------------------*/
  226.  
  227. p8data=p8data+4;
  228.  
  229. while( p8data < fbase+fsize )
  230.   {
  231.   p8Chunk=(Chunk *)p8data;
  232.  
  233.   switch(p8Chunk->ckID)
  234.     {
  235.     case VHDR:
  236.       /*------------------------------------------------*/
  237.       /* Get a pointer to the 8SVX header for later use */
  238.       /*------------------------------------------------*/
  239.       pVoice8Header=(Voice8Header *)(p8data+8L);
  240.       break;
  241.     case BODY:
  242.  
  243.       /*-------------------------------------------------*/
  244.       /* Create pointers to 1-shot and continuous parts  */
  245.       /* for the top octave and get length. Store them.  */
  246.       /*-------------------------------------------------*/
  247.         psample[0] = (BYTE *)(p8data + 8L);
  248.         psample[1] = psample[0] + pVoice8Header->oneShotHiSamples;
  249.         length[0] = (ULONG)pVoice8Header->oneShotHiSamples;
  250.         length[1] = (ULONG)pVoice8Header->repeatHiSamples;
  251.         break;
  252.  
  253.     default:
  254.       break;
  255.     }
  256.  
  257.     /* end switch */
  258.  
  259.   p8data   =p8data    + 8L + p8Chunk->ckSize;
  260.  
  261.   if(p8Chunk->ckSize&1L ==1) p8data++;
  262.   }
  263.  
  264. /* Play either the one-shot or continuous, not both */
  265. if  (length[0]==0)   y=1;
  266. else                 y=0;
  267.  
  268. /*---------------------------------------*/
  269. /* Allocate chip memory for samples and  */
  270. /* copy from read buffer to chip memory. */
  271. /*---------------------------------------*/
  272. if(length[y]<=102400)ssize=length[y];
  273. else                 ssize=102400;
  274.  
  275. sbase=(UBYTE *)AllocMem( ssize , MEMF_CHIP | MEMF_CLEAR);
  276. if(sbase==0)   {kill8svx("No chip memory.\n");return(1L);}
  277. CopyMem(psample[y],sbase,ssize);
  278. psample[y]+=ssize;
  279.  
  280. /*----------------------------------*/
  281. /* Calculate playback sampling rate */
  282. /*----------------------------------*/
  283. speed =  clock / pVoice8Header->samplesPerSec;
  284.  
  285. /*-------------------*/
  286. /* Bump our priority */
  287. /*-------------------*/
  288. mt=FindTask(NULL);
  289. oldpri=SetTaskPri(mt,21);
  290.  
  291. /*---------------------------------------------*/
  292. /* Allocate audio I/O blocks and make a port   */
  293. /*---------------------------------------------*/
  294. AudioIOBptr1=(struct IOAudio *)
  295.       AllocMem( sizeof(struct IOAudio),MEMF_CHIP|MEMF_PUBLIC|MEMF_CLEAR);
  296. if(AudioIOBptr1==0) {kill8svx("No IO memory\n");return(1L);}
  297.  
  298. AudioIOBptr2=(struct IOAudio *)
  299.       AllocMem( sizeof(struct IOAudio),MEMF_CHIP|MEMF_PUBLIC|MEMF_CLEAR);
  300. if(AudioIOBptr2==0) {kill8svx("No IO memory\n");return(1L);}
  301.  
  302. port=CreatePort(0,0);
  303. if(port==0){kill8svx("No port\n"); return(1L);}
  304. c=0;
  305.  
  306. while(device!=0)
  307.   {
  308.   /*---------------------------------------*/
  309.   /* Set up audio I/O block for channel    */
  310.   /* allocation and Open the audio device  */
  311.   /*---------------------------------------*/
  312.   AudioIOBptr1->ioa_Request.io_Message.mn_ReplyPort   = port;
  313.   AudioIOBptr1->ioa_Request.io_Message.mn_Node.ln_Pri = 128;
  314.   AudioIOBptr1->ioa_AllocKey                          = 0;
  315.   AudioIOBptr1->ioa_Data                              = chans[c];
  316.   AudioIOBptr1->ioa_Length                            = 1;
  317.  
  318.   device=OpenDevice("audio.device",0,AudioIOBptr1,0);
  319.   c++;
  320.   }
  321. if(device!=0){kill8svx("No channel\n"); return(1L);}
  322.  
  323. /*-------------------------------------------*/
  324. /* Set Up Audio IO Blocks for Sample Playing */
  325. /*-------------------------------------------*/
  326. AudioIOBptr1->ioa_Request.io_Command                =CMD_WRITE;
  327. AudioIOBptr1->ioa_Request.io_Flags                  =ADIOF_PERVOL;
  328.  
  329. /*--------*/
  330. /* Volume */
  331. /*--------*/
  332. AudioIOBptr1->ioa_Volume=60;
  333. /*---------------*/
  334. /* Period/Cycles */
  335. /*---------------*/
  336. AudioIOBptr1->ioa_Period =(UWORD)speed;
  337. AudioIOBptr1->ioa_Cycles =1;
  338. *AudioIOBptr2 = *AudioIOBptr1;
  339. /*--------*/
  340. /*  Data  */
  341. /*--------*/
  342. AudioIOBptr1->ioa_Data            =(UBYTE *)sbase;
  343. AudioIOBptr2->ioa_Data            =(UBYTE *)sbase + 51200;
  344.  
  345.  
  346.  
  347. Aptr=AudioIOBptr2;
  348. /*-----------------*/
  349. /*  Run the sample */
  350. /*-----------------*/
  351. if(length[y]<=102400)
  352.   {
  353.   AudioIOBptr1->ioa_Length=length[y];    /* No double buffering needed */
  354.   BeginIO(AudioIOBptr1);                 /* Begin the sample, wait for */
  355.   wakebit=0L;                            /* it to finish, then quit.   */
  356.   wakebit=Wait(1 << port->mp_SigBit);
  357.   msg=GetMsg(port);
  358.   }
  359. else
  360.   {
  361.   length[y]-=102400;                    /* It's a real long sample so  */
  362.   AudioIOBptr1->ioa_Length=51200L;      /* double buffering is needed  */
  363.   AudioIOBptr2->ioa_Length=51200L;
  364.   BeginIO(AudioIOBptr1);                /* Queue up two samples and Wait */
  365.   BeginIO(AudioIOBptr2);                /* for the first to finish.      */
  366.   while(length[y]>0)                    /* Reuse the Audio IOB, queue it */
  367.     {                                   /* up again and wait for the 2nd */
  368.     wakebit=Wait(1 << port->mp_SigBit); /* Audio IOB to finish.  Reuse   */
  369.     msg=GetMsg(port);                   /* the 2nd, queue it up, repeat. */
  370.  
  371.     if(Aptr==AudioIOBptr1)Aptr=AudioIOBptr2;
  372.     else                  Aptr=AudioIOBptr1;
  373.   
  374.     if(length[y]<=51200)  Aptr->ioa_Length=length[y];
  375.     else                  Aptr->ioa_Length=51200L;
  376.  
  377.     CopyMem(psample[y],Aptr->ioa_Data,Aptr->ioa_Length);
  378.  
  379.     length[y]-=Aptr->ioa_Length;
  380.     psample[y]+=51200;
  381.     BeginIO(Aptr);
  382.     }
  383.   wakebit=Wait(1 << port->mp_SigBit);
  384.   msg=GetMsg(port);
  385.   wakebit=Wait(1 << port->mp_SigBit);
  386.   msg=GetMsg(port);
  387.   }
  388. kill8();
  389. return(0L);
  390.  
  391. }
  392.  
  393.  
  394. /*----------------*/
  395. /* Abort the Read */
  396. /*----------------*/
  397. void
  398. kill8svx(kill8svxstring)
  399. char *kill8svxstring;
  400. {
  401.    puts(kill8svxstring);
  402.    kill8();
  403. }
  404. /*-------------------------*/
  405. /* Return system resources */
  406. /*-------------------------*/
  407. void
  408. kill8()
  409. {
  410.    if(v8handle!=0)Close(v8handle);
  411.    if(fbase !=0)  FreeMem(fbase,fsize);
  412.    if(sbase !=0)  FreeMem (sbase, ssize);
  413.  
  414.    if(device ==0)      CloseDevice(AudioIOBptr1);
  415.    if( port  !=0)      DeletePort(port);
  416.    if(AudioIOBptr1!=0) FreeMem( AudioIOBptr1,sizeof(struct IOAudio) );
  417.    if(AudioIOBptr2!=0) FreeMem( AudioIOBptr2,sizeof(struct IOAudio) );
  418. }
  419.